package com.basker.pisces.core.serialize.xml;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URL;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.io.SAXReader;

import com.basker.pisces.core.meta.IDataObjectMeta;
import com.basker.pisces.core.meta.annotation.DataObjectMeta;
import com.basker.pisces.exception.ExceptionFactory;
import com.basker.pisces.resource.StringResources;

/**
 * 对象Xml序列化器，以对象的{@link IDataObjectMeta}为结构参照，进行序列化和反序列化操作
 *
 * @author hangwen
 */
public class DataContractXmlSerializer {

    private IDataContractXmlSerializeHandler serializerHandler;

    public DataContractXmlSerializer() {
        this(new DefaultXmlSerializeHandler(true));
    }

    public DataContractXmlSerializer(boolean writeClassName) {
        this(new DefaultXmlSerializeHandler(writeClassName));
    }

    /**
     * 针对特定类的序列化器，beanType必须有{@link DataObjectMeta}注解
     *
     * @param beanType
     */
    public DataContractXmlSerializer(Class<?> beanType) {
        this(new BeanXmlSerializeHandler(beanType));
    }

    public DataContractXmlSerializer(IDataContractXmlSerializeHandler serializerHandler) {
        this.serializerHandler = serializerHandler;
    }

    /**
     * 从document读取xml,并反序列化成对象
     *
     * @param document
     * @return
     */
    public Object deserializeFromDocument(Document document) {
        return deserializeFromDocument(document, null);
    }

    /**
     * 从document读取xml,并将其内容附加到dataObject上
     *
     * @param document
     * @param dataObject
     * @return
     */
    public Object deserializeFromDocument(Document document, Object dataObject) {
        DataContractXmlReader xmlReader = new DataContractXmlReader(document);
        xmlReader.setSerializeHandler(serializerHandler);
        return xmlReader.read(dataObject);
    }

    /**
     * 从文件读取xml,并反序列化成对象
     *
     * @param file
     * @return
     */
    public Object deserializeFromFile(File file) {
        return deserializeFromFile(file, null);
    }

    /**
     * 从文件读取xml,并将其内容附加到dataObject上
     *
     * @param file
     * @param dataObject
     * @return
     */
    public Object deserializeFromFile(File file, Object dataObject) {
        try {
            Document document = new SAXReader().read(file);
            return deserializeFromDocument(document, dataObject);
        } catch (DocumentException e) {
            throw this.createDocumentRuntimeException(e, file);
        }
    }

    /**
     * 从reader读取xml,并反序列化成对象
     *
     * @param reader
     * @return
     */
    public Object deserializeFromReader(Reader reader) {
        return deserializeFromReader(reader, null);
    }

    /**
     * 从reader读取xml,并将其内容附加到dataObject上
     *
     * @param reader
     * @param dataObject
     * @return
     */
    public Object deserializeFromReader(Reader reader, Object dataObject) {
        try {
            Document document = new SAXReader().read(reader);
            return deserializeFromDocument(document, dataObject);
        } catch (DocumentException e) {
            throw this.createDocumentRuntimeException(e, reader);
        }
    }

    /**
     * 从字符串读取xml,并反序列化成对象
     *
     * @param xml
     * @return
     */
    public Object deserializeFromString(String xml) {
        return deserializeFromString(xml, null);
    }

    /**
     * 从字符串读取xml,并将其内容附加到dataObject上
     *
     * @param xml
     * @param dataObject
     * @return
     */
    public Object deserializeFromString(String xml, Object dataObject) {
        try {
            Document document = DocumentHelper.parseText(xml);
            return deserializeFromDocument(document, dataObject);
        } catch (DocumentException e) {
            throw this.createDocumentRuntimeException(e, xml);
        }

    }

    /**
     * 从url读取xml,并反序列化成对象
     *
     * @param url
     * @return
     */
    public Object deserializeFromUrl(URL url) {
        return deserializeFromUrl(url, null);
    }

    /**
     * 从url读取xml,并将其内容附加到dataObject上
     *
     * @param url
     * @param dataObject
     * @return
     */
    public Object deserializeFromUrl(URL url, Object dataObject) {
        try {
            Document document = new SAXReader().read(url);
            return deserializeFromDocument(document, dataObject);
        } catch (DocumentException e) {
            throw this.createDocumentRuntimeException(e, url);
        }
    }

    /**
     * 将对象序列化到文件
     *
     * @param file
     * @param dataObject
     * @return
     */
    public File serializeToFile(File file, Object dataObject) {
        return this.serializeToFile(file, dataObject, false, true);
    }

    /**
     * 将对象序列化到文件
     *
     * @param file
     * @param dataObject
     * @param isPrettyFormat 是否美化输出格式
     * @param suspandResovle 是否挂起资源项加载
     * @return
     */
    public File serializeToFile(File file, Object dataObject, boolean isPrettyFormat, boolean suspandResovle) {
        try (Writer writer = new FileWriter(file)) {
            serializeToWriter(writer, dataObject, isPrettyFormat, suspandResovle);
            return file;
        } catch (IOException e) {
            throw ExceptionFactory.createRuntimeException(DataContractXmlSerializeException.class, e);
        }
    }

    /**
     * 将对象序列化成xml字符串
     *
     * @param dataObject
     * @return
     */
    public String serializeToString(Object dataObject) {
        return this.serializeToString(dataObject, false, true);
    }

    /**
     * 将对象序列化成xml字符串
     *
     * @param dataObject
     * @param isPrettyFormat 是否美化输出格式
     * @param suspandResovle 是否挂起资源项加载
     * @return
     */
    public String serializeToString(Object dataObject, boolean isPrettyFormat, boolean suspandResovle) {
        try (Writer writer = new StringWriter()) {
            serializeToWriter(writer, dataObject, isPrettyFormat, suspandResovle);
            return writer.toString();
        } catch (IOException e) {
            // StringWriter.close不会有异常
            return "";
        }
    }

    /**
     * 将对象序列化到writer,外部控制writer对象的声明周期
     *
     * @param writer
     * @param dataObject
     * @return
     */
    public void serializeToWriter(Writer writer, Object dataObject) {
        this.serializeToWriter(writer, dataObject, false, true);
    }

    /**
     * 将对象序列化到writer,外部控制writer对象的声明周期
     *
     * @param writer
     * @param dataObject
     * @param isPrettyFormat 是否美化输出格式
     * @param suspandResovle 是否挂起资源项加载
     * @return
     */
    public void serializeToWriter(Writer writer, Object dataObject, boolean isPrettyFormat, boolean suspandResovle) {
        // TODO 序列化时禁止多语言
        DataContractXmlWriter xmlWrite = new DataContractXmlWriter(writer, isPrettyFormat);
        xmlWrite.setSerializeHandler(serializerHandler);

        try {
            if (suspandResovle) {
                // 取消资源翻译功能
                StringResources.suspandResovle();
            }
            xmlWrite.writeDataContract(dataObject);
        } finally {
            if (suspandResovle) {
                StringResources.resumeResovle();
            }
        }
    }

    private RuntimeException createDocumentRuntimeException(DocumentException e, Object source) {
        throw ExceptionFactory.createRuntimeException(DataContractXmlDeserializeException.class, e,
                "pisces-core.DataContractXmlSerializer.handleDocumentException-001", source, e.getMessage());
    }

}
